﻿using System;
using System.Collections.Generic;
using System.Runtime.Remoting.Contexts;
using System.IO;

namespace vulnanalyzer
{
    class Cernel
    {
        //хранит информацию о всех текущих загрузках
        CernelStorage cernStorage;

        public Cernel()
        {
            FolderMaster.Clear();

            cernStorage = new CernelStorage();
            Initialize();
        }

        private void Initialize()
        {
            cernStorage.allLoadings.Clear();

            List<DirectoryInfo> commons = FolderMaster.GetDownloadingCommonFolder();

            foreach (DirectoryInfo a in commons)
            {
                FolderMaster fm = new FolderMaster(a);
                try
                {
                    Loading currLoad = fm.ReadInitializingFile();

                    Downloader down = new Downloader(currLoad);
                    int i = down.GetAmountOfPart();
                    List<DownloaderPartState> map = fm.GetPartMap(i);
                    ExtendedLoading ex = new ExtendedLoading();
                    ex.load = currLoad;
                    ex.partMap = map;

                    cernStorage.allLoadings.Add(ex);
                }
                catch (Exception e)
                {
                }
            }
        }

        /// <summary>
        /// скачивает отдельный элемент одного файла
        /// </summary>
        /// <returns>если часть скачана возвращяет true, иначе false</returns>
        public bool StartElem()
        {
            bool needDownload = false;

            int numberOfLoading = GetNumberNoWorkExtendingLoading();
            if (numberOfLoading >= 0)
            {
                cernStorage.allLoadings[numberOfLoading].working = true;

                //Если не все части данного файла скачаны
                if (ThisExtendedLoadingNeedDownload(cernStorage.allLoadings[numberOfLoading]))
                {
                    //загрузить следующий кусок
                    try
                    {
                        Downloader down = new Downloader(cernStorage.allLoadings[numberOfLoading].load);
                        int i = FolderMaster.GetNumberNondownloadPart(cernStorage.allLoadings[numberOfLoading].partMap);
                        down.DownloadPartOfFile(i);
                        cernStorage.allLoadings[numberOfLoading].partMap[i] = DownloaderPartState.downloaded;
                    }
                    catch (Exception e)
                    {
                        return false;
                    }
                    finally
                    {
                        cernStorage.allLoadings[numberOfLoading].working = false;
                    }
                }

                //если все части скачаны собираем их в файл
                if (ThisExtendedLoadingHaveAllFile(cernStorage.allLoadings[numberOfLoading]))
                {
                    FolderMaster fm = new FolderMaster(cernStorage.allLoadings[numberOfLoading].load);
                    try
                    {
                        fm.BuildAllFile();
                        needDownload = true;
                    }
                    catch (Exception e)
                    {
                    }
                }
                cernStorage.allLoadings[numberOfLoading].working = false;

                if (needDownload)
                {
                    cernStorage.allLoadings.RemoveAt(numberOfLoading);
                }
            }
            return true;
        }

        /// <summary>
        /// Скачивает и собирает первый файл в очереди
        /// </summary>
        public void Start()
        {
            bool needDownload = false;

            //данная структура, в дальнейшем рассчитана на многопоточное скачивание файлов
            //получаем число указывающее на не загруженный файл
            int numberOfLoading = GetNumberNoWorkExtendingLoading();
            if (numberOfLoading >= 0)
            {
                while (!needDownload)
                {
                    cernStorage.allLoadings[numberOfLoading].working = true;

                    //Если не все части данного файла скачаны
                    if (ThisExtendedLoadingNeedDownload(cernStorage.allLoadings[numberOfLoading]))
                    {
                        //загрузить следующий кусок
                        try
                        {
                            Downloader down = new Downloader(cernStorage.allLoadings[numberOfLoading].load);
                            int i = FolderMaster.GetNumberNondownloadPart(cernStorage.allLoadings[numberOfLoading].partMap);
                            down.DownloadPartOfFile(i);
                            cernStorage.allLoadings[numberOfLoading].partMap[i] = DownloaderPartState.downloaded;
                        }
                        catch (Exception e)
                        {

                        }
                        finally
                        {
                            cernStorage.allLoadings[numberOfLoading].working = false;
                        }
                    }

                    //если все части скачаны собираем их в файл
                    if (ThisExtendedLoadingHaveAllFile(cernStorage.allLoadings[numberOfLoading]))
                    {
                        FolderMaster fm = new FolderMaster(cernStorage.allLoadings[numberOfLoading].load);
                        try
                        {
                            fm.BuildAllFile();
                            needDownload = true;
                        }
                        catch (Exception e)
                        {
                        }
                    }
                    cernStorage.allLoadings[numberOfLoading].working = false;

                    if (needDownload)
                    {
                        cernStorage.allLoadings.RemoveAt(numberOfLoading);
                    }
                }
            }
        }

        /// <summary>
        /// скачивает и собирает все файлы из очереди
        /// </summary>
        public void StartAll()
        {
            while (cernStorage.allLoadings.Count != 0)
            {
                bool needDownload = false;

                //данная структура, в дальнейшем рассчитана на многопоточное скачивание файлов
                //получаем число указывающее на не загруженный файл
                int numberOfLoading = GetNumberNoWorkExtendingLoading();
                if (numberOfLoading >= 0)
                {
                    while (!needDownload)
                    {
                        cernStorage.allLoadings[numberOfLoading].working = true;

                        //Если не все части данного файла скачаны
                        if (ThisExtendedLoadingNeedDownload(cernStorage.allLoadings[numberOfLoading]))
                        {
                            //загрузить следующий кусок
                            try
                            {
                                Downloader down = new Downloader(cernStorage.allLoadings[numberOfLoading].load);
                                int i = FolderMaster.GetNumberNondownloadPart(cernStorage.allLoadings[numberOfLoading].partMap);
                                down.DownloadPartOfFile(i);
                                cernStorage.allLoadings[numberOfLoading].partMap[i] = DownloaderPartState.downloaded;
                            }
                            catch (Exception e)
                            {

                            }
                            finally
                            {
                                cernStorage.allLoadings[numberOfLoading].working = false;
                            }
                        }

                        //если все части скачаны собираем их в файл
                        if (ThisExtendedLoadingHaveAllFile(cernStorage.allLoadings[numberOfLoading]))
                        {
                            FolderMaster fm = new FolderMaster(cernStorage.allLoadings[numberOfLoading].load);
                            try
                            {
                                fm.BuildAllFile();
                                needDownload = true;
                            }
                            catch (Exception e)
                            {
                            }
                        }
                        cernStorage.allLoadings[numberOfLoading].working = false;

                        if (needDownload)
                        {
                            cernStorage.allLoadings.RemoveAt(numberOfLoading);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Добавляет закачку в очередь
        /// </summary>
        /// <param name="uri"></param>
        /// <param name="nameOfFile"></param>
        public void AddLoading(String uri, String nameOfFile)
        {
            Uri ur = new Uri(uri);
            Loading load = new Loading(ur, nameOfFile);
            FolderMaster.AddNewDownloadFolder(load);
            FolderMaster fm = new FolderMaster(load);
            Downloader down = new Downloader(load);

            ExtendedLoading exload = new ExtendedLoading();
            exload.load = load;
            exload.partMap = fm.GetPartMap(down.GetAmountOfPart());

            cernStorage.allLoadings.Add(exload);
        }

        /// <summary>
        /// Добавляет закачку в очередь
        /// </summary>
        /// <param name="uri"></param>
        /// <param name="nameOfFile"></param>
        public void AddLoading(Uri uri, String nameOfFile)
        {
            Loading load = new Loading(uri, nameOfFile);
            FolderMaster.AddNewDownloadFolder(load);
            FolderMaster fm = new FolderMaster(load);
            Downloader down = new Downloader(load);

            ExtendedLoading exload = new ExtendedLoading();
            exload.load = load;
            exload.partMap = fm.GetPartMap(down.GetAmountOfPart());

            cernStorage.allLoadings.Add(exload);
        }

        /// <summary>
        /// Добавляет закачку в очередь
        /// </summary>
        /// <param name="item"></param>
        public void AddLoading(Loading item)
        {
            FolderMaster.AddNewDownloadFolder(item);
            FolderMaster fm = new FolderMaster(item);
            Downloader down = new Downloader(item);

            ExtendedLoading exload = new ExtendedLoading();
            exload.load = item;
            exload.partMap = fm.GetPartMap(down.GetAmountOfPart());

            cernStorage.allLoadings.Add(exload);
        }

        /// <summary>
        /// Добавляет загрузку в очередь, если её там нет и возвращяет true, иначе не добавляет и false
        /// </summary>
        public bool AddLoadingIfNo(String uri, String nameOfFile)
        {
            if (!HasInLoadQuery(uri))
            {
                AddLoading(uri, nameOfFile);
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Добавляет загрузку в очередь, если её там нет и возвращяет true, иначе не добавляет и false
        /// </summary>
        public bool AddLoadingIfNo(Uri uri, String nameOfFile)
        {
            if (!HasInLoadQuery(uri))
            {
                AddLoading(uri, nameOfFile);
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Добавляет загрузку в очередь, если её там нет и возвращяет true, иначе не добавляет и false
        /// </summary>
        public bool AddLoadingIfNo(Loading load)
        {
            if (!HasInLoadQuery(load))
            {
                AddLoading(load);
                return true;
            }
            else
            {
                return false;
            }
        }


        /// <summary>
        /// Возвращяет полное количество частей на которые разобьются файлы загрузки в очереди
        /// </summary>
        /// <returns></returns>
        public int GetNumbersPart()
        {
            int countPart = 0;
            foreach (ExtendedLoading i in cernStorage.allLoadings)
            {
                foreach (DownloaderPartState j in i.partMap)
                {
                    if (j == DownloaderPartState.nonDownloaded)
                        countPart++;
                }
            }
            return countPart;
        }

        /// <summary>
        /// Находит первый по алфавиту файл, который в данный момент не скачивается, и возвращяет его порядковый номер
        /// </summary>
        /// <returns></returns>
        private int GetNumberNoWorkExtendingLoading()
        {
            return cernStorage.allLoadings.FindIndex(ThisExtendedLoadingNotWorking);
        }


        /// <summary>
        /// Проверяет существует ли данная закачка уже в очереди. Возвращяет true если такая загрузка уже есть, иначе false.
        /// </summary>
        /// <param name="uri">Адрес файла скачивания</param>
        /// <returns></returns>
        public bool HasInLoadQuery(String uri)
        {
            foreach (ExtendedLoading exload in cernStorage.allLoadings)
            {
                if (exload.load.Uri.ToString() == uri)
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Проверяет существует ли данная закачка уже в очереди. Возвращяет true если такая загрузка уже есть, иначе false.
        /// </summary>
        /// <param name="uri">Адрес файла скачивания</param>
        /// <returns></returns>
        public bool HasInLoadQuery(Uri uri)
        {
            foreach (ExtendedLoading exload in cernStorage.allLoadings)
            {
                if (exload.load.Uri == uri)
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Проверяет существует ли данная закачка уже в очереди. Возвращяет true если такая загрузка уже есть, иначе false.
        /// </summary>
        /// <param name="load"></param>
        /// <returns></returns>
        public bool HasInLoadQuery(Loading load)
        {
            foreach (ExtendedLoading exload in cernStorage.allLoadings)
            {
                if (exload.load.Uri == load.Uri)
                {
                    return true;
                }
            }
            return false;
        }


        /// <summary>
        /// Проверяет все ли части загружены у данной закачки
        /// </summary>
        /// <param name="item"></param>
        /// <returns>true если все</returns>
        private bool ThisExtendedLoadingHaveAllFile(ExtendedLoading item)
        {
            if (
            FolderMaster.GetNumberDownloadingPart(item.partMap) == -1 &&
            FolderMaster.GetNumberNondownloadPart(item.partMap) == -1)
                return true;
            else return false;
        }             

        /// <summary>
        /// Проверяет нужно ли ещё скачать часть для данной загрузки
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        private bool ThisExtendedLoadingNeedDownload(ExtendedLoading item)
        {
            int i = FolderMaster.GetNumberNondownloadPart(item.partMap);
            if (i == -1)
                return false;
            else return true;
        }

        /// <summary>
        /// показывает работает ли сейчас загрузка данного файла
        /// </summary>
        /// <param name="item"></param>
        /// <returns>работает - true, не работает - false</returns>
        private bool ThisExtendedLoadingNotWorking(ExtendedLoading item)
        {
            return !item.working;
        }

    }
    

    [Synchronization]
    public class CernelStorage : ContextBoundObject
    {
        public List<ExtendedLoading> allLoadings;
        public int amountOfThreads;
        public long sizeOfPartByte;

        public CernelStorage()
        {
            allLoadings = new List<ExtendedLoading>();
            amountOfThreads = 1;
            sizeOfPartByte = 524288;
        }
    }
}
